package com.xcity.game.buildv2;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;

import com.alibaba.fastjson.annotation.JSONField;
import com.xcity.db.entity.Employee;
import com.xcity.db.entity.Player;
import com.xcity.game.assist.AssistBuild;
import com.xcity.game.buildv2.template.BuildTemplateV2;
import com.xcity.game.common.Cause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.conquests.Conquest;
import com.xcity.game.employee.EmployeeQuality;
import com.xcity.game.employee.EmployeeService;
import com.xcity.game.employee.template.EmployeeProduceMoneyByQualityV2Template;
import com.xcity.game.event.GameEvents;
import com.xcity.game.item.DropGroupTemplate;
import com.xcity.game.item.ItemTemplate;
import com.xcity.game.item.drop.Drop;
import com.xcity.game.item.drop.listener.ItemDropListener;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.pomelo.push.SyncPush;
import com.xcity.pomelo.push.msg.BatchItemsPush;
import com.xcity.pomelo.push.sync.CustomSync;
import com.xcity.pomelo.response.BuildTakeExpResponse.ExpDelta;
import com.xcity.pomelo.response.BuildV2Sync;
import com.xcity.util.BackDoorHandler;

import gnu.trove.map.TIntObjectMap;
import naga.x.App;
import naga.x.common.Updatable;
import naga.x.game.template.TemplateService;
import naga.x.game.time.TimeUpdater;

public class BuildV2 implements Updatable {

	@JSONField(name = "0")
	protected int id;
	@JSONField(name = "1")
	protected State state;
	@JSONField(name = "2")
	protected BuildV2Part[] parts;
	@JSONField(name = "3")
	protected long[] employees;
	@JSONField(name = "4")
	protected long createTime;
	@JSONField(name = "5")
	protected long invaderId;
	@JSONField(name = "6")
	protected long capturedOperateTimeout;
	@JSONField(name = "7")
	protected int unTakeMoney;
	@JSONField(name = "8")
	protected long lastTakeMoneyTime;//上一次取钱的时间 用于判断能不能拿钱
	@JSONField(name = "9")
	protected long lastPutExpBookTime;//上一次放技能书的时间 -1为没放书
	@JSONField(name = "10")
	protected long[] lastPutEmployeeTime;//上一次放员工的时间 -1为没放员工
	@JSONField(name = "11")
	protected int putItemId;//放的技能书ID -1为没放书 需要根据lastPutExpTime计算是否过期
	@JSONField(name = "12")
	protected long lastTakeExpTime;//上一次领经验的时间 判断能不能领经验 刚放经验书的时候会记一次 -1为没放书
	@JSONField(name = "13")
	protected long lastStartMakeMoneyTime;//上一次开始产钱的时间 -1不产钱
	@JSONField(name = "14")
	protected long lastCapureTime;//上一次被攻占时间
	@JSONField(name = "15")
	protected long assistId;//协助者ID
	@JSONField(name = "16")
	protected long assistTime;//协助时间
	@JSONField(serialize = false)
	protected BuildManagerV2 vm;
	@JSONField(serialize = false)
	protected BuildTemplateV2 template;
	@JSONField(serialize = false)
	protected int lastTakeMoney;//上一次领过的钱 用于客户端显示
	
	public static final int PART_COUNT = 5; // 部位数量
	public static final int MAX_SLOT = 4; // 最大员工槽位
	public static final int CAPTURE_OPERATE_TIMEOUT = 60000; // 攻占后有效操作的超时时间
	//TODO
	public static final boolean DEBUG = BackDoorHandler.CHEAT_ENABLE;//false;//
	public static final int MAX_TAKE_MONEY_SECOND = DEBUG?300:8 * 3600;//3600;// 最大生产金钱的累计时间(秒)
	public static final int TAKE_EXP_CD_SECOND = DEBUG?120:1800;//1800;//领取经验CD半小时
	public static final int TAKE_MONEY_CD_SECOND = DEBUG?60:60;//1800;//1800;//领取金钱CD半小时
	
	public static final int BOOK_STATE_ERROR = -1;//-1 数据异常
	public static final int BOOK_STATE_CAN_PUT = 0;//0可放置
	public static final int BOOK_STATE_CANNOT_PUT = 1;//1已有书
	
	public static final int BUILD_RETURN_MILLISECOND = DEBUG?600 * 1000:24 * 3600 * 1000;//建筑归还时间 24小时
	
	
	protected static final Logger LOG = BuildServiceV2.LOG;
	
	public static enum State {
		/** 0-未解锁 */
		LOCKED,
		/** 1-可建造 */
		CAN_BUILD,
		/** 2-正在建造 */
		BUILDING,
		/** 3-建造完成 */
		BUILT,
		/** 4-被占领（只有在建造完成后才可能被占领） */
		CAPTURED,
		/** 5-已解锁形象，但不能建造（通关某个关卡后解锁） */
		UNLOCKED,
		/** 6-被协防 */
		ASSISTED,
		
	}
	
	public BuildV2() {}
	
	public BuildV2(BuildManagerV2 vm, BuildTemplateV2 template) {
		this.vm = vm;
		this.template = template;
		this.id = template.getId();
		this.state = BuildUtils.isUnlock(vm.getOwner(), template) ? State.CAN_BUILD : State.LOCKED;
		this.parts = new BuildV2Part[PART_COUNT];
		for (int i = 0; i < PART_COUNT; i++) {
			this.parts[i] = new BuildV2Part(i, template.getPart(i));
		}
		this.createTime = TimeUpdater.getInstance().now();
		this.lastTakeMoneyTime = -1;
		this.lastTakeMoney = -1;
		this.putItemId = -1;
		this.lastTakeExpTime = -1;
		this.lastStartMakeMoneyTime = -1;
		
		this.lastPutEmployeeTime = new long[MAX_SLOT];
	}
	
	public void init() {
		for (int i = 0; i < PART_COUNT; i++) {
			BuildV2Part part = parts[i];
			part.index = i;
			part.template = getTemplate().getPart(i);
		}
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	public BuildV2Part[] getParts() {
		return parts;
	}

	public void setParts(BuildV2Part[] parts) {
		this.parts = parts;
	}
	
	public BuildV2Part getPart(int pos) {
		return pos >= 0 && pos < parts.length ? parts[pos] : null;
	}

	public long[] getEmployees() {
		return employees;
	}

	public void setEmployees(long[] employees) {
		this.employees = employees;
	}

	public long getCreateTime() {
		return createTime;
	}

	public void setCreateTime(long createTime) {
		this.createTime = createTime;
	}

	public long getInvaderId() {
		return invaderId;
	}

	public void setInvaderId(long invaderId) {
		this.invaderId = invaderId;
	}

	public long getCapturedOperateTimeout() {
		return capturedOperateTimeout;
	}

	public void setCapturedOperateTimeout(long capturedOperateTimeout) {
		this.capturedOperateTimeout = capturedOperateTimeout;
	}
	
	
	public long getLastTakeMoneyTime() {
		return lastTakeMoneyTime;
	}

	public void setLastTakeMoneyTime(long lastTakeMoneyTime) {
		this.lastTakeMoneyTime = lastTakeMoneyTime;
	}

	public long getLastPutExpBookTime() {
		return lastPutExpBookTime;
	}

	public void setLastPutExpBookTime(long lastPutExpBookTime) {
		this.lastPutExpBookTime = lastPutExpBookTime;
	}
	
	public long[] getLastPutEmployeeTime() {
		return lastPutEmployeeTime != null ? lastPutEmployeeTime : (lastPutEmployeeTime = new long[MAX_SLOT]);
	}

	public void setLastPutEmployeeTime(long[] lastPutEmployeeTime) {
		this.lastPutEmployeeTime = lastPutEmployeeTime;
	}
	
	public int getPutItemId() {
		return putItemId;
	}

	public void setPutItemId(int putItemId) {
		this.putItemId = putItemId;
	}

	public long getLastTakeExpTime() {
		return lastTakeExpTime;
	}

	public void setLastTakeExpTime(long lastTakeExpTime) {
		this.lastTakeExpTime = lastTakeExpTime;
	}
	
	public long getLastStartMakeMoneyTime() {
		return lastStartMakeMoneyTime;
	}

	public void setLastStartMakeMoneyTime(long lastStartMakeMoneyTime) {
		this.lastStartMakeMoneyTime = lastStartMakeMoneyTime;
	}

	public long getLastCapureTime() {
		return lastCapureTime;
	}

	public void setLastCapureTime(long lastCapureTime) {
		this.lastCapureTime = lastCapureTime;
	}

	public long getAssistId() {
		return assistId;
	}

	public void setAssistId(long assistId) {
		this.assistId = assistId;
	}

	public long getAssistTime() {
		return assistTime;
	}

	public void setAssistTime(long assistTime) {
		this.assistTime = assistTime;
	}

	public BuildManagerV2 getVm() {
		return vm;
	}

	public void setVm(BuildManagerV2 vm) {
		this.vm = vm;
	}

	public BuildTemplateV2 getTemplate() {
		return template != null ? template : (template = App.getApp().getServiceManager().get(TemplateService.class).get(BuildTemplateV2.class, id));
	}

	public void setTemplate(BuildTemplateV2 template) {
		this.template = template;
	}
	
	public void levelUp(int part, String cause) {
		BuildV2Part p = parts[part];
		int oldLvl = p.level++;
		Player player = getVm().getOwner();
		if (p.level > p.maxLevelBeforeBroken) { // 记录该部位达到过的最高等级，以保证只给玩家一次升级经验
			p.maxLevelBeforeBroken = p.level;
			player.addExp(getTemplate().getPartExp(), Cause.BUILD_LEVEL_UP);
		}
		if (p.level >= BuildV2Part.MAX_LEVEL) {
			DropGroupTemplate drop = getTemplate().getPartDoneDrop();
			if (drop != null) {
				final BatchItemsPush sync = new BatchItemsPush(1);
				drop.getDrop().drop(Drop.RND, player, Cause.BUILD_LEVEL_UP, new ItemDropListener() {
					@Override
					public void itemDroped(Player player, int itemId, int count) {
						sync.add(itemId, count);
					}
				});
				if (!sync.isEmpty()) {
					player.send(sync);
				}
			}
			
			if (isBuildDone()) {
				state = State.BUILT;
				getVm().createNextBuilding();
				App.getApp().getEventManager().addEvent(GameEvents.EVENT_BUILDING_FINISH_V2, player, this);
				player.fireFixedAssetsChanged();
			}
		}
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_BUILDING_PART_LEVEL_UP_V2, player, this, p);
		LOG.info("[BUILD V2 LVLUP]ID[{}]BUILD[{}]PART[{}]OLDLVL[{}]LVL[{}]STATE[{}]CAUSE[{}]", player.getId(), id, part, oldLvl, p.level, state, cause);
	}
	
	public void levelDown(int part, String cause) {
		BuildV2Part p = parts[part];
		int oldLvl = p.level--;
		LOG.info("[BUILD V2 LVLDOWN]ID[{}]BUILD[{}]PART[{}]OLDLVL[{}]LVL[{}]STATE[{}]CAUSE[{}]", getVm().getOwner().getId(), id, part, oldLvl, p.level, state, cause);
	}
	
	// 检测该建筑是否建成
	@JSONField(serialize = false)
	public boolean isBuildDone() {
		for (int i = parts.length - 1; i >= 0; i--) {
			BuildV2Part p = parts[i];
			if (p.level < BuildV2Part.MAX_LEVEL) {
				return false;
			}
		}
		return true;
	}
	
	public void addEmployee(Employee employee, int pos) {
		if (pos < 0 || pos >= MAX_SLOT) {
			return;
		}
		if (employees == null) {
			employees = new long[MAX_SLOT];
		}
		long oldEmployeeId = employees[pos];
		if (oldEmployeeId != 0) {
			Employee oldEmployee = currentOwner().getEmployees().get(oldEmployeeId);
			if (oldEmployee != null) {
				oldEmployee.setState(com.xcity.db.entity.Employee.State.FREE, true);
			}
		}
		employee.setState(com.xcity.db.entity.Employee.State.DEFENSE, true);
		employees[pos] = employee.getId();
		

		if(isEmployeeFull()){
			//建筑产钱开始计时
			this.unTakeMoney = 0;
			this.lastStartMakeMoneyTime = TimeUpdater.getInstance().now();
		}else{
			this.lastStartMakeMoneyTime = -1;
		}
		if(this.checkExpBookState() == BuildV2.BOOK_STATE_CANNOT_PUT){
			//建筑产经验开始计时
			this.lastPutEmployeeTime[pos] = TimeUpdater.getInstance().now();
		}
	}
	
	public int removeEmployee(Employee employee, ExpDelta expDelta) {
		int ret = -1;
		if (employees == null) {
		}else{
			//判断召回员工之前 建筑是否是满建筑
			if(isEmployeeFull()){
				//是满建筑要先结算钱
				calcMoney();
			}
			for (int i = 0; i < employees.length; i++) {
				if (employees[i] == employee.getId()) {
					//处理产经验
					if(canGetExpBySlotIndex(i)){
						int addExp = this.calcEmployeeExpBySlotIndex(i);
						if(addExp > 0){
							employee.addExp(addExp, Cause.BUILD_ADDEXP);
							if(expDelta != null){
								expDelta.addExp = addExp;
								expDelta.newExp = employee.getExp();
								expDelta.newLvl = employee.getLevel();
							}
						}
					}
					this.lastPutEmployeeTime[i] = -1;
					//处理产经验结束
					employees[i] = 0;
					employee.setState(com.xcity.db.entity.Employee.State.FREE, true);
					ret = i;
					break;
				}
			}
			if(ret != -1){
				String error = this.takeMoney(false, true);
				this.lastStartMakeMoneyTime = -1;
//				if(error == null && addMoney > 0){
//					//TODO 可能要弹什么?
//				}
			}
//			this.lastTakeMoneyTime = TimeUpdater.getInstance().now();
//			this.unTakeMoney = 0;
			
			tryRemoveExpBook();
		}
		return ret;
	}
	
	/**
	 * 移除该建筑内的员工
	 * @param expDelta 经验变化 如果是空 不计算经验变化
	 * @return ret 移除了几个员工
	 */
	public int removeAllEmployee(ExpDelta expDelta){
		int ret = 0;
		if(employees == null){
			employees = new long[MAX_SLOT];
			return ret;
		}
		if(isEmployeeFull()){
			calcMoney();
		}
		for(int i = 0; i < employees.length; i++){
			Employee e = this.getVm().owner.getEmployees().get(employees[i]);
			if(e != null){
				//处理产经验
				if(canGetExpBySlotIndex(i)){
					int addExp = this.calcEmployeeExpBySlotIndex(i);
					if(addExp > 0){
						e.addExp(addExp, Cause.BUILD_ADDEXP);
						if(expDelta != null){
							expDelta.addExp += addExp;
							expDelta.newExp = e.getExp();
							expDelta.newLvl = e.getLevel();
						}
					}
				}
				this.lastPutEmployeeTime[i] = -1;
				employees[i] = 0;
				e.setState(com.xcity.db.entity.Employee.State.FREE, true);
				ret++;
			}
		}
		if(ret == 4){
			String error = this.takeMoney(false, true);
			this.lastStartMakeMoneyTime = -1;
		}
		tryRemoveExpBook();
		return ret;
	}
	
	public Player currentInvader() {
		if (state == State.CAPTURED && invaderId != 0L) {
			Player p = App.getApp().getServiceManager().get(PlayerCacheService.class).find(invaderId);
			if (p == null) {
				state = State.BUILT;
				invaderId = 0L;
			}
			return p;
		}
		return null;
	}
	
	public Player currentOwner() {
		Player owner = null;
		if (state == State.CAPTURED) {
			owner = currentInvader();
		}
		if( state == State.ASSISTED){
			owner = currentAssistant();
		}
		return owner != null ? owner : getVm().getOwner();
	}
	
	public Player currentAssistant(){
		if(state == State.ASSISTED && assistId != 0L){
			Player p = App.getApp().getServiceManager().get(PlayerCacheService.class).find(assistId);
			if (p == null) {
				state = State.BUILT;
				assistId = 0L;
			}
			return p;
		}
		return null;
	}
	
	/**
	 * 获取当前驻守员工
	 * @return
	 */
	public long[] currentEmployees() {
		Player invader;
		Conquest c;
		if (state == State.CAPTURED
				&& (invader = currentInvader()) != null
				&& (c = invader.getConquests().find(getVm().getOwner().getId(), getId())) != null) {
			return c.getEmployees();
		}
		if(state == State.ASSISTED && ((invader = currentAssistant()) != null)){
			AssistBuild assbuild = invader.getAssists().findByOwnerId(this.getVm().getOwner().getId());
			if(assbuild != null){
				return assbuild.getEmployees();
			}
		}
		return getEmployees();
	}
	
	/**
	 * 被攻打后更新一些数据
	 * @param player
	 */
	public void beAttacked(Player player) {
		// force give up capture for old invader
		Player oldInvader = currentInvader();
		if (oldInvader != null) {
			unCapture(oldInvader, Cause.FORCE, true);
		}
		Player assist = currentAssistant();
		if(assist != null){
			unAssisted(assist, true, Cause.ASS_CAPTURE);
		}
		
		state = State.BUILT;
		Player source = getVm().getOwner();
		if (player.getId() != source.getId()) {
			invaderId = player.getId();
			capturedOperateTimeout = TimeUpdater.getInstance().now() + CAPTURE_OPERATE_TIMEOUT;
		} else {
			invaderId = 0L;
			capturedOperateTimeout = 0L;
		}
		source.fireFixedAssetsChanged();
	}
	
	public synchronized long[] capture(Player player, List<Employee> employees, String cause) {
		if (player.getId() != invaderId || state != State.BUILT || wasCapturedOperateTimeout()) {
			return null;
		}
		Conquest c = player.getConquests().add(this, employees);
		lastCapureTime = TimeUpdater.getInstance().now();
		state = State.CAPTURED;
		c.tryStartMakeMoney();
		Player source = getVm().getOwner();
//		if (source instanceof Actor) {
//			((Actor) source).setDirty(true);
//		}
		if (source.isOnline()) {
			source.send(new CustomSync(SyncPush.SYNC_BUILD, new BuildV2Sync(this)));
		}
		source.fireFixedAssetsChanged();
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_BUILDING_CAPTURE, player, this);
		LOG.info("[BUILD CAPTURE]ID[{}]DEST[{}]BUILD[{}]EMPLOYEES[{}]CAUSE[{}]", player.getId(), source.getId(), getId(), c != null ? c.getEmployees() : null, cause);
		//被占领后处理产钱
		//TODO
//		if(employees.size() == 4){
//			//建筑产钱开始计时
//			this.unTakeMoney = 0;
//			this.lastStartMakeMoneyTime = TimeUpdater.getInstance().now();
//		}else{
//			this.lastStartMakeMoneyTime = -1;
//		}
		return null;
	}
	
	public synchronized long[] unCapture(Player player, String cause, boolean needCalcMoney) {
		if (player.getId() != invaderId) {
			return null;
		}
		Conquest c = player.getConquests().remove(this, needCalcMoney);
		state = State.BUILT;
		invaderId = 0L;
		lastCapureTime = 0L;
		capturedOperateTimeout = 0L;
//		if (player instanceof Actor) {
//			((Actor) player).setDirty(true);
//		}
		Player source = getVm().getOwner();
//		if (source instanceof Actor) {
//			((Actor) source).setDirty(true);
//		}
		if (source.isOnline()) {
			source.send(new CustomSync(SyncPush.SYNC_BUILD, new BuildV2Sync(this)));
		}
		source.fireFixedAssetsChanged();
		LOG.info("[BUILD UNCAPTURE]ID[{}]DEST[{}]BUILD[{}]RESULT[{}]CAUSE[{}]", player.getId(), source.getId(), getId(), c != null ? 1 : 0, cause);
		return c != null ? c.getEmployees() : null;
	}
	
	public boolean wasCapturedOperateTimeout() {
		return capturedOperateTimeout == 0L || capturedOperateTimeout < TimeUpdater.getInstance().now();
	}

	@Override
	public boolean update() {
		if (state == State.BUILT && invaderId != 0L && wasCapturedOperateTimeout()) {
			invaderId = 0L;
			capturedOperateTimeout = 0L;
		}
		if(invaderId > 0){
			if(lastCapureTime > 0){
				long timeNow = TimeUpdater.getInstance().now();
				if(timeNow - lastCapureTime > BUILD_RETURN_MILLISECOND){
					Player invader = this.currentInvader();
					if(invader != null){
//						Conquest con = invader.getConquests().find(this.getVm().getOwner().getId(), id);
						this.unCapture(invader, Cause.AUTO, true);
					}
				}
			}else{
				lastCapureTime = TimeUpdater.getInstance().now();
			}
		}
		return true;
	}
	
	public boolean updateAssist(){
		if(assistId > 0){
			if(assistTime > 0){
				long timeNow = TimeUpdater.getInstance().now();
				if(timeNow - assistTime > BUILD_RETURN_MILLISECOND){
					Player owner = this.getVm().getOwner();
					Player assistant = this.currentAssistant();
					//TODO
					if(assistant != null){
						AssistBuild assbuild = assistant.getAssists().tryRemoveAssistBuild(owner.getId(), this.id);
						if(assbuild != null){
							//TODO 给奖励
							assbuild.takeMoney(assistant, timeNow, this, true);
//							int[] ret = assbuild.calcMoney(timeNow, this.getTemplate().getProduceMoneyFactor());
//							if(ret[0] > 0){
//								MailAttach attach = new MailAttach(ret[0], 0);
//								String title = MessageConst.ASSIST_BUILD_TAKE_MONEY;
//								String content = MessageFormat.format(MessageConst.ASSIST_BUILD_TAKE_MONEY_CONTENT, this.getTemplate().getName(), ret[0]);
//								App.getApp().getServiceManager().get(MailService.class).sendSystemMail(owner.getId(), title, content, attach);
//							}
//							if(ret[1] > 0){
//								MailAttach attach = new MailAttach(ret[1], 0);
//								String title = MessageConst.ASSIST_BUILD_TAKE_MONEY_ASS;
//								String content = MessageFormat.format(MessageConst.ASSIST_BUILD_TAKE_MONEY_CONTENT_ASS, owner.getName(), ret[1]);
//								App.getApp().getServiceManager().get(MailService.class).sendSystemMail(assistant.getId(), title, content, attach);
//							}
						}
						this.reset();
						if(getVm().getOwner().isOnline()){
							getVm().getOwner().send(new CustomSync(SyncPush.SYNC_BUILD, new BuildV2Sync(this)));
						}
					}
				}
			}
		}
		return true;
	}

	/**
	 * 从建筑生产钱
	 * @param needCalcMoney 是否需要重新计算钱
	 * @param isAuto 是否是召回员工的时候产钱
	 * @return
	 */
	public String takeMoney(boolean needCalcMoney, boolean isAuto){
		if(!isAuto){
			long now = TimeUpdater.getInstance().now();
			if(this.lastStartMakeMoneyTime <= 0){
				return MessageConst.BUILD_CANNOT_TAKE;
			}else if(!isEmployeeFull()){
				return MessageConst.BUILD_EMPLOYEE_IS_NOT_FULL;
			}
			if(this.lastTakeMoneyTime > 0){
				long lastStart = lastTakeMoneyTime;
				if(lastStart < this.lastStartMakeMoneyTime){
					lastStart = this.lastStartMakeMoneyTime;
				}
				if(now - lastStart < BuildV2.TAKE_MONEY_CD_SECOND * 1000){
					return MessageConst.BUILD_TAKE_MONEY_NOT_TIME_UP;
				}
			}else{
				if(now - this.lastStartMakeMoneyTime < BuildV2.TAKE_MONEY_CD_SECOND * 1000){
					return MessageConst.BUILD_TAKE_MONEY_NOT_TIME_UP;
				}
			}
		}
		if(needCalcMoney){
			this.calcMoney();
		}
		this.realTakeMoney(isAuto);
		this.lastTakeMoneyTime = TimeUpdater.getInstance().now();
		this.unTakeMoney = 0;
		return null;
	}
	
	/**
	 * 加钱
	 * @param isAuto 是否是召回员工的时候产钱
	 */
	public void realTakeMoney(boolean isAuto){
		if(unTakeMoney > 0){
			//领钱
			if(isAuto){
				currentOwner().addGold(unTakeMoney, Cause.BUILD_PRODUCE_MONEY_AUTO);
			}else{
				currentOwner().addGold(unTakeMoney, Cause.BUILD_PRODUCE_MONEY);
			}
			LOG.info("[BUILD TAKEMONEY]ID[{}]BUILD[{}]MONEY[{}]CAUSE[{}]", currentOwner().getId(), this.getId(), unTakeMoney, Cause.BUILD_PRODUCE_MONEY);
			this.lastTakeMoney = unTakeMoney;
//			//领完钱置0
//			if(!isAuto){
//				//手动领钱记CD
//				this.lastTakeMoneyTime = this.lastStartMakeMoneyTime;
//			}
//			unTakeMoney = 0;
		}
	}
	
	public int unTakeMoney(){
		return unTakeMoney;
	}
	
	/**
	 * 领经验
	 * @return 结果数组 [0]0失败 1成功
	 * [i * 3 + 1]第i个员工加的经验
	 * [i * 3 + 2]第i个员工当前经验
	 * [i * 3 + 3]第i个员工当前等级
	 */
	public int[] takeExp(){
		int lenth = employees.length * 3 + 1;
		int[] result = new int[lenth];
		for(int i = 0; i < employees.length; i++){
			//每个员工算一次
			if(canGetExpBySlotIndex(i)){
				int addExp = calcEmployeeExpBySlotIndex(i);
				if(addExp > 0){
					Employee employee = currentOwner().getEmployees().get(employees[i]);
					if(employee.addExp(addExp, Cause.BUILD_ADDEXP_TAKE)){
						result[0] = 1;//成功
						result[i * 3 + 1] = addExp;
						result[i * 3 + 2] = employee.getExp();
						result[i * 3 + 3] = employee.getLevel();
					}
				}
			}
		}
		if(result[0] == 1){
			this.lastTakeExpTime = TimeUpdater.getInstance().now();
			tryRemoveExpBook();
		}
		return result;
	}
	
	/**
	 * 计算累计应获得多少
	 * @return 本次计算后累计增加的钱
	 */
	public int calcMoney(){
		if(this.lastStartMakeMoneyTime <= 0){
			unTakeMoney = 0;
			return 0;
		}
		long elapseMS = TimeUpdater.getInstance().now() - this.lastStartMakeMoneyTime;
		if(elapseMS < 0){
			unTakeMoney = 0;
			return 0;
		}
//		if(elapseMS > MAX_TAKE_MONEY_SECOND * 1000){
//			elapseMS = MAX_TAKE_MONEY_SECOND * 1000;
//		}
		Map<EmployeeQuality, EmployeeProduceMoneyByQualityV2Template> quality2Temp = App.getApp().getServiceManager().get(EmployeeService.class)
				.configure().getCommonConfigure().getQuality2produceMoneyTemplate();
		int addMoneyPerMinute = 0;
		long[] eids = currentEmployees();
		Player player = currentOwner();
		for(int i = 0; i < eids.length; i++){
			long eid = eids[i];
			Employee e = player.getEmployees().get(eid);
			if(e != null){
				EmployeeQuality quality = e.getTemplate().getQuality();
				EmployeeProduceMoneyByQualityV2Template temp = quality2Temp.get(quality);
				if (temp != null) {
					addMoneyPerMinute = addMoneyPerMinute + temp.getProduceMoney();
				}
			}
		}
		//秒  * (所有人金钱/分钟和) * 建筑系数 / 60秒
		int addMoney = (int) (elapseMS / 60L / 1000L * addMoneyPerMinute * this.getTemplate().getProduceMoneyFactor() / 100L);
		int oldUntakeMoney = unTakeMoney;
		unTakeMoney = unTakeMoney + addMoney;//unTakeMoney + addMoney;
		int maxMoney = addMoneyPerMinute * MAX_TAKE_MONEY_SECOND / 60 * this.getTemplate().getProduceMoneyFactor() / 100;
		if(unTakeMoney > maxMoney){
			unTakeMoney = maxMoney;
		}
		this.lastStartMakeMoneyTime = TimeUpdater.getInstance().now();
		return unTakeMoney - oldUntakeMoney;
	}
	
	@JSONField(serialize = false)
	public boolean isEmployeeFull(){
		long[] eids = getEmployees();
		if (eids == null) {
			return false;
		}
		Player player = getVm().getOwner();
		for (int i = 0; i < eids.length; i++) {
			if (eids[i] == 0 || player.getEmployees().get(eids[i]) == null) {
				return false;
			}
		}
		return true;
	}
	/**
	 * 检查建筑中经验书的状态
	 * @return -1 数据异常 0可放置 1已有书 
	 */
	public int checkExpBookState(){
		int ret = BOOK_STATE_ERROR;
		if(putItemId == -1 || putItemId == 0){
			ret = BOOK_STATE_CAN_PUT;
		}else{
			if(lastPutExpBookTime <= 0L){
				ret = BOOK_STATE_CAN_PUT;
			}else{
				TIntObjectMap<ItemTemplate> itemTemps = App.getApp().getServiceManager().get(TemplateService.class).getTemplates(ItemTemplate.class);
				ItemTemplate itemTemplate = itemTemps.get(putItemId);
				if(itemTemplate != null){
					//判断时间
					int putSecond = itemTemplate.getParameter(1, 0) * 60;
					if(putSecond == 0){
						ret = BOOK_STATE_CAN_PUT;
					}else{
						long elapseMS = TimeUpdater.getInstance().now() - this.lastPutExpBookTime;
						if(elapseMS > putSecond * 1000L){
							//已经过期了
							ret = BOOK_STATE_CANNOT_PUT;
						}else if(elapseMS >= 0L){
							//未过期
							ret = BOOK_STATE_CANNOT_PUT;
						}else{
							//能走进这?
							ret = BOOK_STATE_ERROR;
						}
					}
				}else{
					ret = BOOK_STATE_ERROR;
				}
			}
		}
		return ret;
	}
	
	//建筑里有员工
	public boolean hasEmployee() {
		long[] eids = getEmployees();
		for (int i = 0, len = eids != null ? eids.length : 0; i < len; i++) {
			if (eids[i] != 0) {
				return true;
			}
		}
		return false;
	}

	public void realPutExpBook(ItemTemplate itemTemp){
		this.lastPutExpBookTime = TimeUpdater.getInstance().now();
		this.putItemId = itemTemp.getId();
		this.lastTakeExpTime = this.lastPutExpBookTime;
		for(int i = 0; i < this.lastPutEmployeeTime.length; i++){
			this.lastPutEmployeeTime[i] = this.lastPutExpBookTime;
		}
		LOG.info("[BUILD V2 PUT EXP BOOK]ID[{}]OWNER[{}]BUILD[{}]BOOKID[{}]", currentOwner().getId(),vm.owner.getId(), id, this.putItemId);
	}
	
	@Deprecated
	/**
	 * 计算产钱时间(秒)
	 * return 0~3600 如果为-1则不产钱
	 */
	public int calcTakeMoneySecond(){
		int second = -1;
		if(this.getLastTakeMoneyTime() != 0L){
			//判断员工已满
			if(this.isEmployeeFull()){
				second = (int)((TimeUpdater.getInstance().now() - this.getLastTakeMoneyTime())/1000);
				if(second > BuildV2.MAX_TAKE_MONEY_SECOND){
					second = BuildV2.MAX_TAKE_MONEY_SECOND;
				}
			}else{
				second = -1;
			}
		}else{
			second = -1;
		}
		return second;
	}
	
	/**
	 * 距离下次领钱还有多少秒
	 * @return
	 */
	public int nextTakeMoneyLeftSecond(){
		int ret = -1;
		if(lastStartMakeMoneyTime <= 0){
		}else if(!isEmployeeFull()){
		}else{
			long now = TimeUpdater.getInstance().now();
			long lastStart = lastStartMakeMoneyTime;
			if(lastTakeMoneyTime <= 0){
			}else{
				if(lastTakeMoneyTime > lastStart){
					lastStart = lastTakeMoneyTime;
				}
			}
			long elapse = now - lastStart;
			if(elapse > TAKE_MONEY_CD_SECOND * 1000){
				//可以领
				ret = 0;
			}else if(elapse < 0){
				ret = -1;
			}else{
				ret = TAKE_MONEY_CD_SECOND - (int)(elapse / 1000);
			}
		}
		return ret;
	}
	
	/**
	 * 距离上次产钱多长时间
	 * @return
	 */
	public int calcTakeMoneyElapseSecond(){
		int ret = -1;
		if(lastStartMakeMoneyTime > 0L){
			ret = (int) ((TimeUpdater.getInstance().now() - lastStartMakeMoneyTime)/1000);
		}
		return ret;
	}
	
	@Deprecated
	/**
	 * 距离上次领取经验多少秒 用于判断是否可领 -1不可领
	 * @return
	 */
	public int calcTakeExpElapseSecond(){
		int ret = 0;
		if(this.getLastTakeExpTime() != 0){
			ret = (int)((TimeUpdater.getInstance().now() - this.getLastTakeExpTime())/1000);
		}else{
			ret = -1;
		}
		return ret;
	}
	
	/**
	 * 领取经验剩余秒数 0到时 -1不可领
	 * @return
	 */
	public int calcTakeExpLeftSecond(){
		int ret = -1;
		ItemTemplate item = tryGetExpBookTemplate();
		if(item != null){
			int secondCD = TAKE_EXP_CD_SECOND;
			if(this.getLastTakeExpTime() > 0){
				int elapseSecond = (int)((TimeUpdater.getInstance().now() - this.getLastTakeExpTime())/1000);
				if(elapseSecond > secondCD){
					ret = 0;
				}else if(elapseSecond >= 0){
					ret = secondCD - elapseSecond;
				}
			}
		}
		return ret;
	}
	
	@Deprecated
	/**
	 * 距离上次放置经验书多少秒 用于判断技能书什么时候耗完 -1没放书
	 * @return
	 */
	public int calcLastPutExpBookSecond(){
		int ret = -1;
		if(this.getLastPutExpBookTime() != 0){
			ret = (int)((TimeUpdater.getInstance().now() - this.getLastPutExpBookTime())/1000);
		}else{
			ret = -1;
		}
		return ret;
	}

	/**
	 * 经验书剩余秒数 0 剩余0秒 -1没放书
	 * @return
	 */
	public int calcExpBookLeftSecond(){
		int ret = -1;
		ItemTemplate item = tryGetExpBookTemplate();
		if(item != null){
			int totalSecond = item.getParameter(1, 0) * 60;
			if(totalSecond > 0){
				if(this.getLastPutExpBookTime() > 0){
					int elapseSecond = (int)((TimeUpdater.getInstance().now() - this.getLastPutExpBookTime())/1000);
					if(elapseSecond > totalSecond){
						ret = 0;
					}else if(elapseSecond >= 0){
						ret = totalSecond - elapseSecond;
					}
				}
			}
		}
		return ret;
	}
	
	/**
	 * 员工应获得经验
	 * @param slotIndex 员工槽索引 0~3
	 * @return
	 */
	public int calcEmployeeExpBySlotIndex(int slotIndex){
		int ret = 0;
		if(this.lastPutEmployeeTime[slotIndex] > 0 && this.lastTakeExpTime > 0){
			//上一次结算的时间
			long lastCalcTime = this.lastPutEmployeeTime[slotIndex] > this.lastTakeExpTime ? this.lastPutEmployeeTime[slotIndex] : this.lastTakeExpTime;
			long elapseTime = TimeUpdater.getInstance().now() - lastCalcTime;
			ItemTemplate bookTemp = tryGetExpBookTemplate();
			if(bookTemp != null){
				int bookTotalExp = bookTemp.getParameter(0, 0);
				int bookTotalSecond = bookTemp.getParameter(1, 0) * 60;
				if(bookTotalSecond == 0){
					ret = 0;
				}else{
					//这个员工放了多长时间 / 书总时间 * 书总经验
					if(elapseTime >= bookTotalSecond * 1000){
						//放的时间超过了总时间 则按总经验算
						ret = bookTotalExp;
					}else if(elapseTime < 0){
						ret = 0;
					}else{
						ret = (int)(elapseTime / 1000 * bookTotalExp / (bookTotalSecond));
					}
				}
			}
		}
		return ret;
	}
	
	/**
	 * 员工已经放进去多久了
	 * @param slotIndex 员工槽索引 0~3
	 * @return
	 */
	public int employeePutSecondBySlotIndex(int slotIndex){
		int ret = 0;
		long lastPutTime = this.getLastPutEmployeeTime()[slotIndex];
		if(lastPutTime > 0){
			ret = (int)((TimeUpdater.getInstance().now() - lastPutTime)/1000);
		}else{
			ret = -1;
		}
		return ret;
	}
	
	/**
	 * 尝试拿到建筑里的经验书模版
	 * @return ItemTemplate
	 */
	public ItemTemplate tryGetExpBookTemplate(){
		ItemTemplate temp = null;
		if(this.getPutItemId() > 0){
			TIntObjectMap<ItemTemplate> itemTemps = App.getApp().getServiceManager().get(TemplateService.class).getTemplates(ItemTemplate.class);
			temp = itemTemps.get(this.getPutItemId());
		}
		return temp;
	}

	/**
	 * 该槽位是否可以获得经验
 	 * @param slotIndex
	 * @return
	 */
	public boolean canGetExpBySlotIndex(int slotIndex){
		boolean ret = false;
		if(this.lastPutEmployeeTime[slotIndex] <= 0){
			ret = false;
		}else if(this.lastPutExpBookTime <= 0){
			ret = false;
		}else{
			Employee employee = currentOwner().getEmployees().get(employees[slotIndex]);
			if(employee == null){
				ret = false;
			}else{
				ItemTemplate bookTemp =	tryGetExpBookTemplate();
				if(bookTemp == null){
					ret = false;
				}else{
					//超时了
					if(this.lastPutEmployeeTime[slotIndex] > this.lastPutExpBookTime + bookTemp.getParameter(1, 0) * 60000){
						ret = false;
					}else{
						ret = true;
					}
				}
			}
		}
		return ret;		
	}
	/**
	 * 是否可以领取经验
	 * @return 0可以 1没有书 2没有员工 3没到领取时间 4???
	 */
	public int canTakeExp(){
		int ret = 0;
		ItemTemplate itemTemp = tryGetExpBookTemplate();
		long timenow = TimeUpdater.getInstance().now();
		if(itemTemp == null){
			ret = 1;
		}else{
			if(this.lastPutExpBookTime <= 0){
				ret = 1;
			}else if(!hasEmployee()){
				ret = 2;
			}else if(this.lastTakeExpTime <= 0){
				ret = 1;
			}else{
				long takeElapseTime = timenow - this.lastTakeExpTime;
				if(takeElapseTime < 0){
					ret = 4;
				}else if(takeElapseTime < TAKE_EXP_CD_SECOND * 1000){
					//未到领取时间 判断书到没到时间
					if(this.lastPutExpBookTime + itemTemp.getParameter(1, 0) * 60000 > timenow){
						//没到时间
						ret = 3;
					}
				}
				
			}
		}
		return ret;
	}
	
	public int printAndResetLastTakeMoney(){
		int oldTakeMoney = lastTakeMoney;
		lastTakeMoney = -1;
		return oldTakeMoney;
	}
	/**
	 * 尝试移除经验书
	 * @return 0未移除 1移除了
	 */
	public int tryRemoveExpBook(){
		int ret = 0;
		if(hasEmployee()){
			//有员工判断员工入坑时间
			ItemTemplate itemTemp = tryGetExpBookTemplate();
			if(itemTemp == null){
				lastPutExpBookTime = -1;
				putItemId = -1;
				lastTakeExpTime = -1;
				ret = 1;
			}else if(lastPutExpBookTime <= 0){
				lastPutExpBookTime = -1;
				putItemId = -1;
				lastTakeExpTime = -1;
				ret = 1;
			}else if(lastTakeExpTime <= 0){
				lastPutExpBookTime = -1;
				putItemId = -1;
				lastTakeExpTime = -1;
				ret = 1;
			}else if(putItemId <= 0){
				lastPutExpBookTime = -1;
				putItemId = -1;
				lastTakeExpTime = -1;
				ret = 1;
			}else{
				long totalTime = itemTemp.getParameter(1, 0) * 60000;
				
				if(lastTakeExpTime >= lastPutExpBookTime + totalTime){
					//最后一次领经验把所有经验都领完了
					lastPutExpBookTime = -1;
					putItemId = -1;
					lastTakeExpTime = -1;
					ret = 1;
				}else{
					boolean isLate = true;
					for(int i = 0; i < lastPutEmployeeTime.length; i++){
						if(lastPutEmployeeTime[i] > 0){
							if(lastPutEmployeeTime[i] < lastPutExpBookTime + totalTime){
								isLate = false;
							}
						}
					}
					if(isLate){
						//所有员工都在书过期之后入坑
						lastPutExpBookTime = -1;
						putItemId = -1;
						lastTakeExpTime = -1;
						ret = 1;
					}
				}
			}
		}else{
			//没员工直接移除经验书
			lastPutExpBookTime = -1;
			putItemId = -1;
			lastTakeExpTime = -1;
			ret = 1;
		}
		return ret;
	}
	/**
	 * 被协助
	 * @return
	 */
	public int beAssisted(long assistant, long time){
		this.takeMoney(true, false);
		this.assistId = assistant;
		this.assistTime = time;
		this.state = State.ASSISTED;
		this.lastStartMakeMoneyTime = 0;
		return this.lastTakeMoney;
	}
	
	//取消协助防守
	public int[] unAssisted(Player assistant, boolean needSync, String cause){
		int[] ret = new int[]{0, 0};
		if(assistant.getId() != assistId){
			return ret;
		}
		long now = TimeUpdater.getInstance().now();
		AssistBuild assbuild = assistant.getAssists().tryRemoveAssistBuild(getVm().getOwner().getId(), this.id);
		if(assbuild != null){
			ret = assbuild.takeMoney(assistant, now, this, cause == Cause.ASS_CAPTURE);
//			int[] ret = assbuild.calcMoney(now, this.getTemplate().getProduceMoneyFactor());
//			if(ret[0] > 0){
//				MailAttach attach = new MailAttach(ret[0], 0);
//				String title = MessageConst.ASSIST_BUILD_TAKE_MONEY;
//				String content = MessageFormat.format(MessageConst.ASSIST_BUILD_TAKE_MONEY_CONTENT, this.getTemplate().getName(), ret[0]);
//				App.getApp().getServiceManager().get(MailService.class).sendSystemMail(getVm().getOwner().getId(), title, content, attach);
//			}
//			if(ret[1] > 0){
//				MailAttach attach = new MailAttach(ret[1], 0);
//				String title = MessageConst.ASSIST_BUILD_TAKE_MONEY_ASS;
//				String content = MessageFormat.format(MessageConst.ASSIST_BUILD_TAKE_MONEY_CONTENT_ASS, getVm().getOwner().getName(), ret[1]);
//				App.getApp().getServiceManager().get(MailService.class).sendSystemMail(assistant.getId(), title, content, attach);
//			}
		}
		this.reset();
		if(needSync && getVm().getOwner().isOnline()){
			getVm().getOwner().send(new CustomSync(SyncPush.SYNC_BUILD, new BuildV2Sync(this)));
//			getVm().getOwner().send(new MapSync(SyncPush.SYNC_BUILD_STATE, this.getId(), this.getState()));
		}
		LOG.info("[ASS CANCEL]ID[{}]BUILD[{}]ASSID[{}]MONEY[{}]CAUSE[{}]", vm.getOwner().getId(), this.id, assistant.getId(), ret[0], cause);
		return ret;
	}
	
	/**
	 * 该建筑是否可被协助
	 */
	public String canBeAssisted(){
		if(getState() == State.CAPTURED){
			return MessageConst.ASSIST_BUILD_CAPTURED;
		}else if(getState() == State.ASSISTED){
			return MessageConst.ASSIST_HAS_ASSISTED;
		}else if(getState() == State.BUILT){
			if(assistId > 0){
				return MessageConst.ASSIST_HAS_ASSISTED;
			}
			return null;
		}
		return MessageConst.ASSIST_BUILD_BUILDING;
	}
	
	/**
	 * 检测该建筑是否可被攻击（至少有一个部位等级>0）
	 * @return
	 */
	public boolean canBeAttacked() {
		if (getState() == State.BUILDING) {
			BuildV2Part[] parts = getParts();
			for (BuildV2Part part : parts) {
				if (part.getLevel() > 0) {
					return true;
				}
			}
		}
		return false;
	}
	
	public boolean hasEmployee(long employeeId) {
		long[] ids = getEmployees();
		if (employeeId == 0 || ids == null) {
			return false;
		}
		for (int i = ids.length - 1; i >= 0; i--) {
			if (ids[i] == employeeId) {
				return true;
			}
		}
		return false;
	}
	
	public void reset(){
//		assistTime = 0;
		assistId = 0;
		state = State.BUILT;
		if(isEmployeeFull()){
			this.unTakeMoney = 0;
			this.lastStartMakeMoneyTime = TimeUpdater.getInstance().now();
		}else{
			this.unTakeMoney = 0;
			this.lastStartMakeMoneyTime = -1;
		}
	}
}
